package jp.bluecab.bluecabsystem.gps;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import jp.bluecab.bluecabsystem.classes.GPSRecord;
import jp.bluecab.bluecabsystem.util.SdLog;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Toast;
import android.widget.ToggleButton;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class MainActivity extends MapActivity implements android.view.View.OnClickListener {
	protected static final String TAG = "MainActivity";
	
	private MapView mMapView;
	MapController mapController;
	PositionOverlay positionOverlay;
	ToggleButton currentLocationToggle;
	
	private IGPSService gpsService = null;
	protected Calendar actionHistoryFrom;
	private Context mApplicationContext;
	
	private Handler mHandler = new Handler();
	
	private static final int CONNECT = 0;
	private static final int DISCONNECT = 1;
	
	private static final int MENU_APPPREFERENCES = Menu.FIRST;
	private static final int MENU_ACTION_HISTORY = Menu.FIRST + 1;
	private static final int MENU_END_ACTION_HISTORY = Menu.FIRST + 2;
	
	/*
	 * 進行状況表示ダイアログの設定
	 */
	private Handler mProgressHandler = new Handler() {
		
		@Override
		public void handleMessage(Message msg) { // (3) from
			super.handleMessage(msg);
			
			if (msg.what == CONNECT) {
				setProgressBarIndeterminateVisibility(true);
			} else {
				setProgressBarIndeterminateVisibility(false);
			}
		} // (3) to
	};
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);// 必ず必要なスーパークラスのメソッド呼び出し
		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);// タイトルバー表示
		requestWindowFeature(Window.FEATURE_LEFT_ICON);
		setContentView(R.layout.main);// あらかじめ定義しておいたレイアウトを設定
		getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.blue_icon);
		
		// MapViewを取得する
		mMapView = (MapView) findViewById(R.id.myMapView);
		mapController = mMapView.getController();
		
		// 地図表示設定
		mMapView.setSatellite(false);
		mMapView.setStreetView(false);
		mMapView.setClickable(true);
		mMapView.setBuiltInZoomControls(true);
		
		// 地図への情報表示
		Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.current_location);
		positionOverlay = new PositionOverlay(bmp);
		
		List<Overlay> overlays = mMapView.getOverlays();
		overlays.add(positionOverlay);
		
		// 地図の拡大率を設定する
		mapController.setZoom(17);
		
		// サービスにbindする
		Intent intent = new Intent(IGPSService.class.getName());
		bindService(intent, gpsServiceConn, BIND_AUTO_CREATE);
		
		// リソースIDからボタンのインスタンスを取得
		currentLocationToggle = (ToggleButton) findViewById(R.id.btn_update);
		// ボタンのイベントハンドラ(クリックのハンドラ)を設定
		currentLocationToggle.setOnClickListener(this);
		currentLocationToggle.setChecked(true);
		
		mApplicationContext = this.getApplicationContext();
	}
	
	// イベントが通知されたら
	@Override
	public void onClick(View v) {
		
		if (!currentLocationToggle.isChecked()) return;
		
		// 最適なGPS取得方法を選択
		Criteria criteria = new Criteria();
		criteria.setAltitudeRequired(false);
		criteria.setBearingRequired(true);
		criteria.setCostAllowed(true);
		criteria.setSpeedRequired(true);
		
		LocationManager mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		String provider = mLocationManager.getBestProvider(criteria, true);
		mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		Location location = mLocationManager.getLastKnownLocation(provider);
		
		if (location == null) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setTitle("更新失敗");
			builder.setMessage("位置情報の取得に失敗しました");
			builder.setPositiveButton("OK", new android.content.DialogInterface.OnClickListener() {
				public void onClick(android.content.DialogInterface dialog, int whichButton) {
					setResult(RESULT_OK);
				}
			});
			builder.create();
			builder.show();
			return;
		}
		
		double lat = location.getLatitude();
		double lon = location.getLongitude();
		double alt = location.getAltitude();
		
		positionOverlay.setCurrentLocation(lat, lon, alt);
		animateMap(lat, lon);
		
	}
	
	/*
	 * サービスへの接続・切断
	 */
	private ServiceConnection gpsServiceConn = new ServiceConnection() {
		
		/*
		 * サービス接続時
		 */
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.v(TAG, "onServiceConnected() called");
			gpsService = IGPSService.Stub.asInterface(service);
			
			try {
				gpsService.registerUpdateLocationCallback(updateLocationCallback);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		
		/*
		 * サービスからの切断時
		 */
		public void onServiceDisconnected(ComponentName name) {
			Log.v(TAG, "onServiceDisonnected() called");
			
			try {
				gpsService.unreigsterUpdateLocationCallback(updateLocationCallback);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	};
	
	private IUpdateLocationCallback updateLocationCallback = new IUpdateLocationCallback.Stub() {
		
		@Override
		// 現在位置の更新時
		public void updateLocation(double latitude, double longitude, double altitude) throws RemoteException {
			
			// SdLog.put("IUpdateLocationCallback:start");
			
			// 地図上の現在地更新
			positionOverlay.setCurrentLocation(latitude, longitude, altitude);
			
			// マップ上の位置を更新する
			animateMap(latitude, longitude);
			
			Log.v(TAG, "upateLocation:" + latitude + ":" + longitude + ":" + altitude);
		}
		
		@Override
		// 方位の更新時
		public void updateOrientation(float orientation) throws RemoteException {
			positionOverlay.setOrientation(orientation);
			
			// 再描画
			mMapView.invalidate();
			
		}
	};
	
	public void animateMap(double latitude, double longitude) {
		
		if (!currentLocationToggle.isChecked()) return;
		
		Toast.makeText(this, "animateMap起動", Toast.LENGTH_SHORT).show();
		
		// マップ上の位置を更新する
		Double geoLat = latitude * 1E6;
		Double geoLng = longitude * 1E6;
		GeoPoint point = new GeoPoint(geoLat.intValue(), geoLng.intValue());
		
		// 指定位置まで地図をスクロール
		mapController.animateTo(point);
	}
	
	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
	
	/*
	 * メニュー作成
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		
		menu.clear();
		if (positionOverlay.isDisplayActionHistory()) {
			// 行動履歴表示中
			menu.add(0, MENU_APPPREFERENCES, Menu.NONE, R.string.menu_apppreferences).setIcon(android.R.drawable.ic_menu_preferences);
			menu.add(0, MENU_END_ACTION_HISTORY, Menu.NONE, R.string.menu_end_action_history).setIcon(android.R.drawable.ic_menu_close_clear_cancel);
		} else {
			// 行動履歴非表示
			menu.add(0, MENU_APPPREFERENCES, Menu.NONE, R.string.menu_apppreferences).setIcon(android.R.drawable.ic_menu_preferences);
			menu.add(0, MENU_ACTION_HISTORY, Menu.NONE, R.string.menu_action_history).setIcon(android.R.drawable.ic_menu_myplaces);
		}
		
		return true;
	}
	
	/*
	 * メニュー選択後
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		super.onOptionsItemSelected(item);
		
		switch (item.getItemId()) {
		
		case MENU_APPPREFERENCES: // 設定
			Intent intent = new Intent(this, AppPreferences.class);
			startActivity(intent);
			return true;
			
		case MENU_ACTION_HISTORY: // 行動履歴表示
			AlertDialog.Builder builder = new AlertDialog.Builder(this); // (2)
			// from
			builder.setTitle(R.string.menu_action_history);
			builder.setItems(R.array.action_history_entries, new DialogInterface.OnClickListener() {
				
				// ダイアログから表示
				public void onClick(DialogInterface dialog, int which) {
					
					int[] intary = getResources().getIntArray(R.array.action_history_entryvalues);
					int mHours = intary[which];
					
					final TimeZone utc = TimeZone.getTimeZone("UTC");
					actionHistoryFrom = Calendar.getInstance(utc);
					actionHistoryFrom.add(Calendar.HOUR_OF_DAY, mHours * -1);
					
					getActionHistory();
				}
			});
			builder.show(); // (2) to
			return true;
			
		case MENU_END_ACTION_HISTORY: // 行動履歴非表示
			positionOverlay.setDisplayActionHistory(false);
			mMapView.invalidate();
			break;
		
		default:
			break;
		}
		
		return false;
	}
	
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode != KeyEvent.KEYCODE_BACK) {
			return super.onKeyDown(keyCode, event);
		} else {
			Toast.makeText(mApplicationContext, R.string.key_back_home, Toast.LENGTH_LONG).show();
			return false;
		}
	}
	
	@Override
	protected void onDestroy() {
		
		super.onDestroy();
		try {
			// サービスを終了
			gpsService.stopService();
		} catch (RemoteException e) {
			e.printStackTrace();
		} finally {
			// サービスの開放
			unbindService(gpsServiceConn);
		}
	}
	
	protected void getActionHistory() { // (4) from
		Thread updateThread = new Thread(null, backgroundRefresh, "refresh_actionHistory");
		updateThread.start();
	}
	
	private Runnable backgroundRefresh = new Runnable() {
		public void run() {
			
			mProgressHandler.sendMessage(mProgressHandler.obtainMessage(CONNECT));
			doDrawActionHistory();
			mProgressHandler.sendMessage(mProgressHandler.obtainMessage(DISCONNECT));
		}
	}; // (4) to
	
	/**
	 * InputStreamからStringへ変換する
	 */
	public String convertStreamToString(InputStream is) throws IOException { // (5)
		// from
		if (is != null) {
			Writer writer = new StringWriter();
			
			char[] buffer = new char[1024];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {
					writer.write(buffer, 0, n);
				}
			} finally {
				is.close();
			}
			return writer.toString();
		} else {
			return "";
		}
	} // (5) to
	
	/*
	 * 行動履歴を視覚的に表示
	 */
	private void doDrawActionHistory() { // (6) from
		String serverUrl = AppPreferences.getServerUrl(this.getApplicationContext());
		String loginId = AppPreferences.getLoginId(this.getApplicationContext());
		Date fromDate = actionHistoryFrom.getTime();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/M/d-kk:mm");
		sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
		String dateBeginStr = sdf.format(fromDate);
		
		try {
			String locationFeed = "http://" + serverUrl + "/api?target=1" + "loginid=" + loginId + "&datebegin=" + dateBeginStr;
			HttpParams params = new BasicHttpParams();
			HttpConnectionParams.setConnectionTimeout(params, 10 * 1000); // 接続のタイムアウト
			HttpConnectionParams.setSoTimeout(params, 2 * 1000); // データ取得のタイムアウト
			HttpClient objHttp = new DefaultHttpClient(params);
			
			HttpGet objGet = new HttpGet(locationFeed);
			HttpResponse objResponse = objHttp.execute(objGet);
			
			if (objResponse.getStatusLine().getStatusCode() == 200) {
				// 正常取得
				InputStream objStream = objResponse.getEntity().getContent();
				String jsonString = convertStreamToString(objStream);
				
				Type type = new TypeToken<List<GPSRecord>>() {}.getType();
				
				GsonBuilder gsonBuilder = new GsonBuilder();
				gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
				Gson gson = gsonBuilder.create();
				
				List<GPSRecord> locationList = gson.fromJson(jsonString, type);
				
				if (locationList.size() > 1) {
					positionOverlay.setActionHistory(locationList);
					positionOverlay.setDisplayActionHistory(true);
					final GPSRecord locr = locationList.get(0);
					
					mHandler.post(new Runnable() {
						public void run() {
							
							// マップ上の位置を更新する
							Double geoLat = locr.getLatitude() * 1E6;
							Double geoLng = locr.getLongitude() * 1E6;
							GeoPoint point = new GeoPoint(geoLat.intValue(), geoLng.intValue());
							
							mapController.animateTo(point);
							mMapView.invalidate();
						}
					});
				} else {
					mHandler.post(new Runnable() {
						public void run() {
							Toast.makeText(mApplicationContext, R.string.history_not_found, Toast.LENGTH_LONG).show();
						}
					});
				}
			}
		} catch (SocketTimeoutException e) {
			SdLog.put(e);
			mHandler.post(new Runnable() {
				public void run() {
					Toast.makeText(mApplicationContext, R.string.cannot_connect_server, Toast.LENGTH_LONG).show();
				}
			});
		} catch (IOException e) {
			SdLog.put(e);
			mHandler.post(new Runnable() {
				public void run() {
					Toast.makeText(mApplicationContext, R.string.io_error, Toast.LENGTH_LONG).show();
				}
			});
		} finally {}
	} // (6) to
	
	@Override
	/*
	 * onCreate()、onStart()、onResume()の順番 アプリの開始
	 */
	protected void onResume() {
		super.onResume();
	}
	
	@Override
	protected void onPause() {
		/*
		 * if (mRegisteredSensor) {
		 * mSensorManager.unregisterListener(mSensorAdaptor); mRegisteredSensor
		 * = false; }
		 */

		super.onPause();
	}
	
	/**
	 * センサーの値通知を受け取るリスナ
	 */
	// private class OrientationSensorAdaptor implements SensorEventListener {
	/**
	 * センサーの正確さに変化があったタイミングで通知される
	 * 
	 * @param sensor
	 *            センサー
	 * @param accuracy
	 *            正確さ
	 */
	/*
	 * @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { }
	 * 
	 * @Override public void onSensorChanged(SensorEvent event) { if
	 * (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { // 方位から回転角を設定
	 * float[] values = event.values;
	 * positionOverlay.setOrientation(values[SensorManager.DATA_X]);
	 * 
	 * // 再描画 mMapView.invalidate(); } } }
	 */

}